

import CareraVideoShader from '../shader/CareraVideoShader.glsl?raw'
import ECEF from "./ECFF"


/**
 * @example 
 * let video3d = new DK.CameraVideo(viewer, {
    type : 'video',
    url:"./test.mp4",
    position : {
      x : 117,
      y : 25,
      z : 100
    },
    fov : 40,
    far: 1000,//distance
    rotation:{
      x:-50,//elevation
      y:-90,//azimuth
      z:10
    }
  })
 */

export default class CameraVideo {

  /**
   * 
   * @param {*} viewer 
   * @param {*} param 
   */
  constructor(viewer, param) {

    this.ECEF = new ECEF()

    this.param = param
    var option = this._initCameraParam()

    this._rotation = param.rotation
    this.near = option.near ? option.near : 0.1;
    if (option || (option = {}), this.viewer = viewer, this._cameraPosition = option.cameraPosition, this._position = option.position,
      this.type = option.type, this._alpha = option.alpha || 1, this.url = option.url, this.color = option.color,
      this._debugFrustum = Cesium.defaultValue(option.debugFrustum, !0), this._aspectRatio = option.aspectRatio || this._getWinWidHei(),
      this._camerafov = option.fov || Cesium.Math.toDegrees(this.viewer.scene.camera.frustum.fov), this.texture = option.texture || new Cesium.Texture({
        context: this.viewer.scene.context,
        source: {
          width: 1,
          height: 1,
          arrayBufferView: new Uint8Array([255, 255, 255, 255])
        },
        flipY: !1
      }), this._videoPlay = Cesium.defaultValue(option.videoPlay, !0), this.defaultShow = Cesium.defaultValue(option.show, !0), !this.cameraPosition || !this.position) return void console.log('初始化失败：请确认相机位置与视点位置正确!')

    switch (this.type) {
      default:
      case 'video':
        this.activeVideo(this.url)
        break
      case 'image':
        this.activePicture(this.url)
        this.deActiveVideo()
        break
    }

    this._createShadowMap()
    this._getOrientation()
    this._addCameraFrustum()
    this._addPostProcess()
    this.viewer.scene.primitives.add(this)

  }

  _initCameraParam() {
    var viewPoint = this.ECEF.enu_to_ecef({ longitude: this.param.position.x * 1, latitude: this.param.position.y * 1, altitude: this.param.position.z * 1 },
      { distance: this.param.far, azimuth: this.param.rotation.y * 1, elevation: this.param.rotation.x * 1 });
    var position = Cesium.Cartesian3.fromDegrees(viewPoint.longitude, viewPoint.latitude, viewPoint.altitude);
    var cameraPosition = Cesium.Cartesian3.fromDegrees(this.param.position.x * 1, this.param.position.y * 1, this.param.position.z * 1);
    return {
      type: this.param.type,
      url: this.param.url,
      cameraPosition: cameraPosition,
      position: position,
      alpha: this.param.alpha,
      near: this.param.near,
      fov: this.param.fov,
      debugFrustum: this.param.debugFrustum
    }
  }

  /**
    * 旋转
    */
  _changeRotation(e) {
    if (e) {
      this.param.rotation = e;
      var option = this._initCameraParam();
      this.position = option.position;
    }
  }

  /*
  *获取三维地图容器像素大小
  */
  _getWinWidHei() {
    let viewer = this.viewer.scene;
    return viewer.canvas.clientWidth / viewer.canvas.clientHeight;
  }

  /**
   * 设置video宽高
   */
  _changeVideoWidHei() {
    this.viewer.scene.postProcessStages.remove(this.postProcess),
      this.viewer.scene.primitives.remove(this.cameraFrustum)
    this._createShadowMap(this.cameraPosition, this.position),
      this._getOrientation(),
      this._addCameraFrustum(),
      this._addPostProcess()
  }


  _changeCameraFov() {
    this.viewer.scene.postProcessStages.remove(this.postProcess)
    this.viewer.scene.primitives.remove(this.cameraFrustum),
      this._createShadowMap(this.cameraPosition, this.position),
      this._getOrientation(),
      this._addCameraFrustum(),
      this._addPostProcess()
  }

  // 激活视频
  activeVideo(url) {
    let video = this._createVideoEle(url)
    let that = this

    if (video) {
      this.type = 'video'
      var viewer = this.viewer
      this.activeVideoListener || (this.activeVideoListener = function () {
        that.videoTexture && that.videoTexture.destroy(),
          that.videoTexture = new Cesium.Texture({
            context: viewer.scene.context,
            source: video,
            width: 1,
            height: 1,
            pixelFormat: Cesium.PixelFormat.RGBA,
            pixelDatatype: Cesium.PixelDatatype.UNSIGNED_BYTE
          })
      }),
        viewer.clock.onTick.addEventListener(this.activeVideoListener)
    }
  }

  /**
   * 创建视频标签
   * @param {*} url 
   * @returns 
   */
  _createVideoEle(url) {
    this.videoId = "visualDomId"
    var t = document.createElement("SOURCE")
    t.type = "video/mp4"
    t.src = url
    var i = document.createElement("SOURCE")
    i.type = "video/quicktime"
    i.src = url
    var a = document.createElement("VIDEO")
    return a.setAttribute("autoplay", !0),
      a.setAttribute("loop", !0),
      a.setAttribute("crossorigin", !0),
      a.appendChild(t),
      a.appendChild(i),
      this._videoEle = a,
      a
  }

  /**
   * 销毁视频
   */
  deActiveVideo() {
    if (this.activeVideoListener) {
      this.viewer.clock.onTick.removeEventListener(this.activeVideoListener),
        delete this.activeVideoListener
    }
  }

  /**
   * 图片投放
   */
  activePicture(url) {
    this.videoTexture = this.texture
    let that = this
    let img = new Image;

    img.onload = function () {
      that.type = that.optionType.Image,
        that.videoTexture = new Cesium.Texture({
          context: that.viewer.scene.context,
          source: img
        })
    }

    img.onerror = function () {
      console.log('图片加载失败:' + url)
    }

    img.src = url
  }

  locate() {
    var cameraPosition = Cesium.clone(this.cameraPosition),
      position = Cesium.clone(this.position);
    this.viewer.Camera.position = cameraPosition,
      this.viewer.camera.direction = Cesium.Cartesian3.subtract(position, cameraPosition, new Cesium.Cartesian3(0, 0, 0)),
      this.viewer.camera.up = Cesium.Cartesian3.normalize(cameraPosition, new Cesium.Cartesian3(0, 0, 0))
  }

  update(e) {
    this.viewShadowMap && this.viewer.scene.frameState.shadowMaps.push(this.viewShadowMap) // *重点* 多投影
  }

  /**
   * 销毁所有元素
   */
  destroy() {
    this.viewer.scene.postProcessStages.remove(this.postProcess),
      this.viewer.scene.primitives.remove(this.cameraFrustum),
      this.activeVideoListener && this.viewer.clock.onTick.removeEventListener(this.activeVideoListener),
      this.activeVideoListener && delete this.activeVideoListener,
      delete this.postProcess,
      delete this.viewShadowMap,
      delete this.color,
      delete this.viewDis,
      delete this.cameraPosition,
      delete this.position,
      delete this.alpha,
      delete this._camerafov,
      delete this._cameraPosition,
      delete this.videoTexture,
      delete this.cameraFrustum,
      delete this._videoEle,
      delete this._debugFrustum,
      delete this._position,
      delete this._aspectRatio,
      delete this.url,
      delete this.orientation,
      delete this.texture,
      delete this.videoId,
      delete this.type,
      this.viewer.scene.primitives.remove(this),
      delete this.viewer
  }

  /**
   * 创建shadowMap
   */
  _createShadowMap() {
    var e = this.cameraPosition,
      t = this.position,
      i = this.viewer.scene,
      a = new Cesium.Camera(i)
    a.position = e,
      a.direction = Cesium.Cartesian3.subtract(t, e, new Cesium.Cartesian3(0, 0, 0)), //计算两个笛卡尔的组分差异。
      a.up = Cesium.Cartesian3.normalize(e, new Cesium.Cartesian3(0, 0, 0)); // 归一化
    var n = Cesium.Cartesian3.distance(t, e);
    this.viewDis = n,
      a.frustum = new Cesium.PerspectiveFrustum({
        fov: Cesium.Math.toRadians(this.fov),
        aspectRatio: this.aspectRatio,
        near: this.near,
        far: n
      });
    this.viewShadowMap = new Cesium.ShadowMap({
      lightCamera: a,
      enable: !1,
      isPointLight: !1,
      isSpotLight: !0,
      cascadesEnabled: !1,
      context: i.context,
      pointLightRadius: n
    })
  }
  /**
   * 获取shadowmap位置
   */
  _getOrientation() {
    var e = this.cameraPosition,
      t = this.position,
      i = Cesium.Cartesian3.normalize(Cesium.Cartesian3.subtract(t, e, new Cesium.Cartesian3), new Cesium.Cartesian3),
      a = Cesium.Cartesian3.normalize(e, new Cesium.Cartesian3),
      n = new Cesium.Camera(this.viewer.scene);
    n.position = e,
      n.direction = i,
      n.up = a,
      i = n.directionWC,
      a = n.upWC;
    var r = n.rightWC,
      o = new Cesium.Cartesian3,
      l = new Cesium.Matrix3,
      u = new Cesium.Quaternion;
    r = Cesium.Cartesian3.negate(r, o);
    var d = l;
    Cesium.Matrix3.setColumn(d, 0, r, d),
      Cesium.Matrix3.setColumn(d, 1, a, d),
      Cesium.Matrix3.setColumn(d, 2, i, d);
    var c = Cesium.Quaternion.fromRotationMatrix(d, u);
    return this.orientation = c,
      c
  }

  /**
   * 创建对象
   * @param {*} width 
   * @param {*} height 
   * @returns 
   */
  creacteGeometry(width, height) {
    var hwidth = width / 2.0;
    var hheigt = height / 2.0;
    var positions = new Float64Array([hwidth, 0.0, hheigt, -hwidth, 0.0, hheigt, -hwidth, 0.0, -hheigt, hwidth, 0.0, -hheigt]);
    var sts = new Float32Array([1.0, 1.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0]);
    var indices = new Uint16Array([0, 1, 2, 0, 2, 3]);
    var ge = this._createGeometry(positions, sts, indices);
    return ge;
  }


  _createGeometry(positions, sts, indices) {
    return new Cesium.Geometry({
      attributes: {
        position: new Cesium.GeometryAttribute({
          componentDatatype: Cesium.ComponentDatatype.DOUBLE,
          componentsPerAttribute: 3,
          values: positions
        }),
        normal: new Cesium.GeometryAttribute({
          componentDatatype: Cesium.ComponentDatatype.FLOAT,
          componentsPerAttribute: 3,
          values: new Float32Array([255.0, 0.0, 0.0, 255.0, 0.0, 0.0, 255.0, 0.0, 0.0, 255.0, 0.0, 0.0])
          // values: new Float32Array([0.0, 0.0, 0.0,0.0, 0.0, 0.0,0.0, 0.0, 0.0,0.0, 0.0, 0.0])
        }),
        st: new Cesium.GeometryAttribute({
          componentDatatype: Cesium.ComponentDatatype.FLOAT,
          componentsPerAttribute: 2,
          values: sts
        })
      },
      indices: indices,
      primitiveType: Cesium.PrimitiveType.TRIANGLES,
      vertexFormat: new Cesium.VertexFormat({
        position: true,
        color: true
      }),
      boundingSphere: Cesium.BoundingSphere.fromVertices(positions)
    })
  }


  _addCameraFrustum() {
    var e = this;
    this.cameraFrustum = new Cesium.Primitive({
      geometryInstances: new Cesium.GeometryInstance({
        geometry: new Cesium.FrustumOutlineGeometry({
          origin: e.cameraPosition,
          orientation: e.orientation,
          frustum: this.viewShadowMap._lightCamera.frustum,
          _drawNearPlane: !0
        }),
        attributes: {
          color: Cesium.ColorGeometryInstanceAttribute.fromColor(new Cesium.Color(0, 0.5, 0.5))
        }
      }),
      appearance: new Cesium.PerInstanceColorAppearance({
        translucent: !1,
        flat: !0
      }),
      asynchronous: !1,
      show: this.debugFrustum && this.show
    }),
      this.viewer.scene.primitives.add(this.cameraFrustum)
  }

  // 添加后处理
  _addPostProcess() {
    var e = this,
      i = e.viewShadowMap._isPointLight ? e.viewShadowMap._pointBias : e.viewShadowMap._primitiveBias;
    this.postProcess = new Cesium.PostProcessStage({
      fragmentShader: CareraVideoShader,
      uniforms: {
        mixNum: function () {
          return e.alpha
        },
        stcshadow: function () {
          return e.viewShadowMap._shadowMapTexture
        },
        videoTexture: function () {
          return e.videoTexture
        },
        _shadowMap_matrix: function () {
          return e.viewShadowMap._shadowMapMatrix
        },
        shadowMap_lightPositionEC: function () {
          return e.viewShadowMap._lightPositionEC
        },
        shadowMap_texelSizeDepthBiasAndNormalShadingSmooth: function () {
          var t = new Cesium.Cartesian2;
          return t.x = 1 / e.viewShadowMap._textureSize.x,
            t.y = 1 / e.viewShadowMap._textureSize.y,
            Cesium.Cartesian4.fromElements(t.x, t.y, i.depthBias, i.normalShadingSmooth, this.combinedUniforms1)
        },
        shadowMap_normalOffsetScaleDistanceMaxDistanceAndDarkness: function () {
          return Cesium.Cartesian4.fromElements(i.normalOffsetScale, e.viewShadowMap._distance, e.viewShadowMap.maximumDistance, e.viewShadowMap._darkness, this.combinedUniforms2)
        }

      }
    }),
      this.viewer.scene.postProcessStages.add(this.postProcess)
  }

  _changeCameraPos() {
    this.viewer.scene.postProcessStages.remove(this.postProcess),
      this.viewer.scene.primitives.remove(this.cameraFrustum),
      this.viewShadowMap.destroy(),
      this.cameraFrustum.destroy(),
      this._createShadowMap(this.cameraPosition, this.position),
      this._getOrientation(),
      this._addCameraFrustum(),
      this._addPostProcess()
  }

  _changeViewPos() {
    this.viewer.scene.postProcessStages.remove(this.postProcess),
      this.viewer.scene.primitives.remove(this.cameraFrustum),
      this.viewShadowMap.destroy(),
      this.cameraFrustum.destroy(),
      this._createShadowMap(this.cameraPosition, this.position),
      this._getOrientation(),
      this._addCameraFrustum(),
      this._addPostProcess()
  }

  /**
   * 切换隐藏
   */
  _switchShow() {
    this.show ? !this.postProcess && this._addPostProcess() : (this.viewer.scene.postProcessStages.remove(this.postProcess), delete this.postProcess, this.postProcess = null),
      this.cameraFrustum.show = this.show
  }

  get alpha() {
    return this._alpha
  }

  set alpha(val) {
    this._alpha = val
  }

  get aspectRatio() {
    return this._aspectRatio
  }

  set aspectRatio(val) {
    this._aspectRatio = val
    this._changeVideoWidHei()
  }

  get debugFrustum() {
    return this._debugFrustum
  }

  set debugFrustum(val) {
    this._debugFrustum = val
  }

  get fov() {
    return this._camerafov
  }

  set fov(val) {
    this._camerafov = val
    this._changeCameraFov()
  }

  get cameraPosition() {
    return this._cameraPosition
  }

  set cameraPosition(val) {
    this._cameraPosition = e
    this._changeCameraPos()
  }

  get position() {
    return this._position
  }

  set position(val) {
    this._position = val
    this._changeViewPos()
  }

  get videoPlay() {
    return this._videoPlay
  }

  set videoPlay(val) {
    this._videoPlay = Boolean(val)
    this._videoEle && (this.videoPlay ? this._videoEle.paly() : this._videoEle.pause())
  }

  get params() {
    return this.param
  }

  get show() {
    return this.defaultShow
  }

  set show(val) {
    this.defaultShow = Boolean(val)
    this._switchShow()
  }

  get rotation() {
    return this._rotation
  }

  set rotation(val) {
    this._rotation = val
    this._changeRotation(this._rotation)
  }

}


